/**
 * https://hydro.ac/d/HUNNU/p/DataStructure0011
 * 三维不严格偏序，有重复点
 * 1. 排序时不能加等号，要用严格偏序
 * 2. 重复点在统计时不要计算，最后直接加入结果即可
 * 3. 归并的判断中加入第二维等号判断
 * 4. 第二重归并时，第三维加上等号判断，但又不能全部相等
 * 5. 同一个数组多次统计不同偏序的组合，flag要清空
 */
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;


struct cdq_cdq_t{ // 三维偏序，CDQ套CDQ

using value_t = int;
using cmp_t = function<bool(value_t, value_t)>;

struct _t{
    value_t x;
	value_t y;
	value_t z;
	int flag;
	int pos;
};

struct _t_hash {
    size_t operator () (const _t & t) const {
		return ((size_t)t.x * 13131 + t.y) * 13131 + t.z;
	}
	bool operator () (const _t & a, const _t & b) const {
		return a.x == b.x and a.y == b.y and a.z == b.z;
	}
};

int N;
cmp_t cmp1, cmp2, cmp3;
vector<_t> A, B, C;

/// 计数器
__gnu_pbds::gp_hash_table<_t, int, _t_hash, _t_hash> Cnt;

cdq_cdq_t() = default;

void setCmp(cmp_t c1, cmp_t c2, cmp_t c3) {
	cmp1 = c1; cmp2 = c2; cmp3 = c3;
}

void input(){
	cin >> N;
	A.assign(N, {});
	B.assign(N, {});
	C.assign(N, {});
	int k = 0;
	for(auto & p : A) {
		cin >> p.x >> p.y >> p.z;
		p.pos = k++;
		p.flag = 0;
	}
	return;
}

vector<int> Ans;

void run(){
    /// 清空flag
    for(auto & a : A) a.flag = 0;
    
    Ans.assign(N, 0);
	sort(A.begin(), A.end(), [this](const _t & a, const _t & b){
		if(a.x != b.x) return this->cmp1(a.x, b.x);
		if(a.y != b.y) return this->cmp2(a.y, b.y);
		if(a.z != b.z) return this->cmp3(a.z, b.z);
		if(a.pos != b.pos) return a.pos < b.pos;
		return a.flag < b.flag;
	});

	/// 计数器
	Cnt.clear();
	for(const auto & t : A){
		Cnt[t] += 1;
	}  

	CDQ(0, N - 1);

	/// 把自己重复的加上
	for(int i=0;i<N;++i){
		Ans[A[i].pos] += Cnt[A[i]] - 1;
	}        
}


void CDQ(int s, int e){
	if(s >= e) return;

	int mid = (s + e) >> 1;
	CDQ(s, mid); CDQ(mid + 1, e);

	int u = s, v = mid + 1, t = s;
	while(u <= mid and v <= e){
        if(cmp2(A[u].y, A[v].y) or A[u].y == A[v].y){ // 加上等于号
			B[t] = A[u++];
			B[t++].flag = 1;
		}else{
			B[t] = A[v++];
			B[t++].flag = 0;
		}
	}
	while(u <= mid){
        B[t] = A[u++];
		B[t++].flag = 1;
	}
	while(v <= e){
        B[t] = A[v++];
		B[t++].flag = 0;
	}
	copy(B.begin() + s, B.begin() + e + 1, A.begin() + s);
	CDQ2(s, e);
	return;
}

void CDQ2(int s, int e){
	if(s >= e) return;

	int mid = (s + e) >> 1;
	CDQ2(s, mid); CDQ2(mid + 1, e);
	 
    static auto f = _t_hash(); 
    int cnt = 0; // 记录y符合条件的数量
	int u = s, v = mid + 1, t = s;
	while(u <= mid and v <= e){
		if((cmp3(B[u].z, B[v].z) or B[u].z == B[v].z) and (not f(B[u], B[v]))){
			cnt += B[u].flag;
			C[t++] = B[u++];
		}else{ // 否时才统计, 说明u前面的点有符合条件的
            if(not B[v].flag){
				Ans[B[v].pos] += cnt; // 不能加u - s, 而要加u-s中y也符合条件的
			}
			C[t++] = B[v++];
		}        
	}
	while(u <= mid){
        C[t++] = B[u++];
	}
	while(v <= e){
		if(not B[v].flag){
			Ans[B[v].pos] += cnt;
		}
        C[t++] = B[v++];
	}
	copy(C.begin() + s, C.begin() + e + 1, B.begin() + s);
	return;
}

}CDQ;

int main() {
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    function<bool(int, int)> cmp[2] = {less<int>(), greater<int>()};
	CDQ.input();
	vector<array<int, 8>> ans(CDQ.N);
	for(int i=0;i<2;++i)for(int j=0;j<2;++j)for(int k=0;k<2;++k){
		CDQ.setCmp(cmp[i], cmp[j], cmp[k]);
		CDQ.run();
		for(int u=0;u<CDQ.N;++u){
			ans[u][(i<<2)|(j<<1)|k] = CDQ.Ans[u];
		}
	}
	for(const auto & a : ans){
		cout << a[0];
		for(int i=1;i<8;++i) cout << " " << a[i];
		cout << endl;
	}
    return 0;
}